# -*- coding: utf-8 -*-

import os
import urllib
import re
import math



class parse_c():
    ''' Classe possédant des méthodes d'analyse un texte suivant une liste de mots clés. Méthode
    de calcul utilisée : TFDIF  '''
    
    # Attributs de la classe 
    dico = None
    keys_lemme = None
    dj = None 
    d_idfl = None
    
    # Définition des méthodes 
    
    
    # Récupération en entrée d'une liste d'objets contenant le titre / le sigle / descriptif du cours
    # Recherche de mots clés dans différents textes. Renvoie une liste d'index vers le ou les texte ayant l'indice de 
    # tfidf le plus élevé 
    def process (self, type_key, keys, liste_texte):
        
        ''' Méthode centrale pour l'analyse de textes '''
        
        #Sélection des données dans lesquelles la recherche doit être effectue
        data_texte = []
        if ((type_key == 1) or (type_key == 2) or (type_key == 0)) :
            for sous_liste in liste_texte :
                data_texte.append(sous_liste[type_key])
        # Mise en forme des données 
        self.d_idfl = len(data_texte)    
        # Transformation en lemme des données brutes
        list_dict_data = [] # liste contenant les dictionnaire de chacun des data
        data_lemmes = []
        match_list = []
        for texte in data_texte :
            data_lemmes.append(self.keyword_to_lemme(texte))
        # Transformation en lemme des clés    
        self.keys_lemme = self.keyword_to_lemme(keys)
        # Transformation en dictionnaire 
        for data_lemme in data_lemmes :
            list_dict_data.append(self.dictio_lemme_occur(data_lemme))
        # On match et on crée une liste dictionnaire. Chacun d'eux contient le nombre d'occurence de la clé matché
        for data_list in list_dict_data :
            match_list.append(dict(self.lookup_lemm_in_data(data_list)))
            # Recherche des mots clés dans la liste d'objet fournie
        # Intégration du TFIDF
        
        #calcul du dfj 
        dict_dfj = self.dfj_computation(match_list)
        tfidf_list = self.tfidf_computation (match_list, dict_dfj)
        index_result = self.max_tfdif_finder(tfidf_list)
        return index_result
            
    
    # dfj appele pour chacun des mots clés utilisé : Nombre de document contenant la clé
    def dfj_computation (self, objects_list):
        '''' Calcul du dfj pour la méthode du tfidf '''
        dfj = 0
        local_list = []
        for key in self.keys_lemme :    
            for dictionnaire in objects_list :
                if key in dictionnaire :
                    dfj += 1
            local_list.append((key, dfj))
            dfj = 0
        dict_dfj = dict(local_list)
        return dict_dfj
        
        

    # Méthode de calcul de la valeur d'un tfid. 
    # PAramètres d'entrées : liste contenant des dictionnaires, ainsi qu'un dictionnaire contenant les dfj (voir tfidf)
    def tfidf_computation (self, result_list, dict_dfj):
        #tfij : frequence du terme (term frequency) qui correspond
        #au nombre d'occurences du terme tj dans le
        #document Di
        tfidf_list = []
        tfidf = 0
        # Récupération du dfj 
        # Pour chacun des documents à analyser 

        for dictionnaire in result_list :

            tfidf = 0
            for key in self.keys_lemme :
            # Pour chacun des mots clés on évalue le tfidf puis on somme le tfidf de chaque mot clés
            # et on l'associe à un texte
                if key in dict_dfj:
                    dfj = dict_dfj[key]   
                else :
                    dfj = 0
                if key in dictionnaire :
                    tfij = dictionnaire[key]
                else :
                    tfij = 0
                if ((dfj != 0) and (self.d_idfl != 0)) :
                    idfj = math.log(float(self.d_idfl) / float(dfj))
                else :
                    idfj = 0 
                tfidf += idfj * tfij
            tfidf_list.append(tfidf)
        # On renvoie une liste de tfidf associé à chacun des texte données en entrée (l'association se fait en 
        # gardant le même numéro d'index dans la liste
        return tfidf_list
    
    # Méthode pour retourner une liste d'index vers la valeur de tfidf la plus élevée 
    def max_tfdif_finder(self, liste):
        
        ''' Parcours une liste et renvoie les index de la valeur la plus élevée '''
        max_local = 0
        object_index_list = []
        for tfidf in liste :
            if tfidf > max_local :
                max_local = tfidf
        # On trouve tous les positions des tfidf max
        if max_local != 0 :
            for i in range(0, len(liste)):
                if liste[i] == max_local:
                    object_index_list.append(i)
        return object_index_list
                
                
    # On prend la liste triée composée des lemmes et des mots n'ayant pas de lemme
    # Pour chacun des mots on compte l'occurence et on cré un dictionnaire ayant les caractéristiques suivantes :
    # lemme  : occurence
    # Remarque : 
    # On détermine l'occurence de chacun des termes et on crée un dictionnaire assoicant le lemme et son occurence
    def dictio_lemme_occur(self, liste):
        ''' Crée un dictionnaire de l'occurence des termes contenue dans une liste '''
        temp = []
        for entry in liste :
            occur = liste.count(entry)
            temp.append((entry, occur))
        temp = dict(temp)
        return temp
    
    
    # Fonction de recherche de lemmes dans des données mis sous la forme d'un dictionnaire
    
    def lookup_lemm_in_data(self, data):
        match_list = []
        for key in self.keys_lemme :                                                # Recherche avec la succed liste
            if key in data :
                match_list.append((key, data[key]))
        return match_list
            


    # 2nde fonction de recherche dans un dictionnaire 
    # Paramètres d'entrées : # liste dont les éléments sont les mots à analyser
    #                         # dictionnaire de référence
    
    def last_lookup(self, mot):
        
        # Initialisation
        lemme_list = []                                                           # Gestion des erreurs lors de la recherche dans le dictionnaire                                                                                  # Pour chacun des mots de la liste 
        mot_compose = re.findall(r'[\w]+', mot, re.U)                               # on remplace les ' et - par des espaces et l'on crée une nouvelle liste ERRRREUR                                                                      # Pour chacun des mots contenus dans la liste découpée
        for mot  in mot_compose :
            if (mot == "") :                                                      # On ignore le caractère vide
                continue
            else : 
                try :                                                            # Sinon 
                    lemme_list.append(self.dico[mot])                            # Récupération du nombre de syllabe(s) associées
                except KeyError :                                                # Erreur traitée : mots non présent dans le dico
                    lemme_list.append(mot)                                       # Ajout du mot non traité à la liste de retour
        result_liste = lemme_list                                                # Création de la liste de retour
        return result_liste    
    
        # On essaie de trouver un lemme à partir de la combinaison de deux mots
    
    
    def two_words_in_one_lookup (self, liste):
        
        # Initialisation
        new_lemme = []
        fail_list = []
        if ((liste[0] + " " + liste[1]) in self.dico) :
            new_mot = liste[0] + " " + liste[1]
            new_lemme = self.dico[new_mot]                                     # Calcul du nombre de syllabes correspondantes
        else :                                                                        
            fail_list.append(liste[0])                                              # Sinon on ajoute les deux mots à la liste des mots 
            fail_list.append(liste[1])                                              # non traités  
        return_list = [fail_list, new_lemme]                               # Création de la liste de retour
        return return_list
    
    
    
    # Fonction de transformation d'une chaine de caractère une une liste de lemme associée
    # Paramètres d'entrée : # Liste dont les éléments sont les mots à analyser
    #                       # Dicionnaire 
    

    # Pour chacun des mots clé contenues dans le string passé, on cherche on cherche le lemme associé, et on retourne la liste 
    # des lemmes et la liste des mots dont la recherche de lemme n'a pas abouti
        
    def keyword_to_lemme (self, data_string):
        ''' Transformation via un dictionnaire d'une chaine en lemme '''
        # Mise en forme de la liste 
        data_string = data_string.lower().decode('utf8 ')
        data_string = re.findall(r'[\w\-\']+', data_string, re.UNICODE)
        # Initialisation
        lemme_list = []
        tampon = [''] * 2                                   # Variable tampon pour contenir deux mots à analyser de manière conjointe
        for mot in data_string:                                # Pour chacun des mots contenus dans la liste
            if mot == "":                                # On ignore le caractère vide 
                continue
            elif mot in self.dico :                      # si le mot est dans le dictionnaire
                lemme_list.append(self.dico[mot])        # Calcul du nombre de syllabes
                tampon[0] = mot                           # Mise en mémoire du dernier mot matché
            else :
                tampon[1] = mot                                    # Mise en mémoire du mot non matché
                result = self.two_words_in_one_lookup(tampon)     # Tentative de combiner les deux mots 
                if result[1] != []:                              # Si match alors 
                    try : 
                        lemme_list.remove(tampon[0])
                    except ValueError :
                        print "Un cas bizare"  
                else :
                    result = self.last_lookup(mot)

                    if result != []:
                        for mots in result:
                            lemme_list.append(mots)
                tampon[0] = mot 
        # Mise à jour des valeurs
        return lemme_list
    
    
    # On transofrme les données en entrées vers un format exploitable ie :
    # un dictionnaire avec pour chaque mot le nombre d'itération 
            
    # Fonction de création d'un dictionnaire 
    # Paramètres d'entrées : # lexique
    #                        # Nombre de lignes présentes au début du texte à ignorer
    #                        # Position dans le lexique du mot servant de clé au dictionnaire
    #                        # Position dans le lexique du nombre de syllabe servant de résultat dans le dico
           
    
    
    def create_dictionary(self, file_in, ind1, ind2, ind3):
        ''' Création d'un dictionnaire à partir d'un fichier '''
        liste = file_in.split("\n")
        # Suppression des "ind1" premières colones inutiles (données de type descriptive)
        del liste[:ind1]
        l = []
        for i in liste:                                                     # Pour chacune des lignes 
            i = i.split("\t")                                               # Création d'une liste contenant les différentes info pour chaque mot
            if (len(i) < ind3) :                                            # Vérification en cas d'irrégularité du lexique
                continue
            else :                                                          # Création d'une liste contenant le mots et le nombre de syllabes associées
                l.append((i[ind2], i[ind3]))                                # position 0 et 3 pour nous
        self.dico = dict(l)                                                 # Création du dictionnaire    
        
        
    
    def dictionnaire_init (self):
        #on verifi la presence du dico dans le repertoir de travail. 
        #Si on ne le trouve pas un essai de le recuperer sur le web
        localdic = "./Lexique372.txt"
        if os.path.isfile(localdic):
            print "travail a partir de la version local du dictionnaire"
            file_in = open(localdic)
        else :
            try : 
                urldic = "http://www.cours.polymtl.ca/inf8007/Tp/20121/Lexique372.txt"
                print "recuperation de la version distante du dictionnaire..."
                print urldic
                file_in = urllib.urlopen(urldic)
            except :
                print "erreur lors de la tentative de recuperation du dictionnaire !"
                return False
            
        file_in = file_in.read().decode('latin1 ').encode ('utf8 ').decode('utf8')

        
        self.create_dictionary(file_in, 0, 0, 2) # lemme est la troisième entrée
        return True

###### Pour des fins de tests ci_desous #####

if __name__ == '__main__':
    print "test du module de parse..."
    data = ["Projet individuel choisi par l'étudiant et réalisé sous la direction d'un professeur du département de génie mécanique ", "ou de tout autre ingénieur agréé par le département. La responsabilité de l'ensemble du travail est assumée par l'étudiant. ", "Le projet doit être approuvé par le responsable du projet intégrateur III du département de génie mécanique. Le projet fait l'objet d'un rapport ", "qui doit présenter l'état de la question, le développement de la solution choisie, les résultats et les conclusions.", " Le rapport doit aussi contenir une section sur la gestion du projet ainsi qu'une section décrivant les matières intégrées lors du projet. Le projet fait l'objet d'une présentation orale publique devant un jury d'évaluation composé de professeurs."]
    keys = " Projet mécanique"
    analyser = parse_c()
    if analyser.dictionnaire_init():
        print "< OK >"
    analyser.process(3, keys, data)
        
